home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / MATHS / RLAB / RLAB125.ZIP / !RLaB / misc / MAC / getline2.c < prev    next >
C/C++ Source or Header  |  1994-06-26  |  37KB  |  1,263 lines

  1. #ifndef lint
  2. static char     rcsid[] =
  3. "getline2.c,v 1.1.1.1 1994/02/21 08:05:52 ian Exp";
  4. static char    *copyright = "Copyright (C) 1991, 1992, 1993, Chris Thewalt";
  5. #endif
  6.  
  7. /*
  8.  * Minor mods for RLaB by Ian Searle, 5/18/93
  9.  */
  10. #include "rlab.h"
  11. #ifndef HAVE_SIZE_T
  12. #define size_t unsigned int
  13. #endif
  14.  
  15. #ifdef __STDC__
  16. #define  rstrstr  strstr
  17. #else
  18. static char * rstrstr _PROTO ((CONST char *CONST haystack, 
  19.                               CONST char *CONST needle));
  20. #endif
  21.  
  22. /*
  23.  * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
  24.  *
  25.  * Permission to use, copy, modify, and distribute this software 
  26.  * for any purpose and without fee is hereby granted, provided
  27.  * that the above copyright notices appear in all copies and that both the
  28.  * copyright notice and this permission notice appear in supporting
  29.  * documentation.  This software is provided "as is" without express or
  30.  * implied warranty.
  31.  *
  32.  * Thanks to the following people who have provided enhancements and fixes:
  33.  *   Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
  34.  *   DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
  35.  *   Edin Hodzic, Eric J Bivona, Kai Uwe Rommel
  36.  */
  37.  
  38. #include       "getline2.h"
  39. static int      gl_tab();  /* forward reference needed for gl_tab_hook */
  40.  
  41. /******************** imported interface *********************************/
  42.  
  43. #include <string.h>
  44. #include <ctype.h>
  45. #include <errno.h>
  46. #include <signal.h>
  47. #ifdef THINK_C
  48. #include <stdlib.h>
  49. #include <myconsole16.h>
  50. #else
  51. extern int      isatty();       
  52. extern void    *malloc();
  53. extern void     free();
  54. extern int      kill(); 
  55. #endif
  56. /********************* exported interface ********************************/
  57.  
  58. char           *getline();              /* read a line of input */
  59. void            gl_setwidth();          /* specify width of screen */
  60. void            gl_histadd();           /* adds entries to hist */
  61. void            gl_strwidth();          /* to bind gl_strlen */
  62.  
  63. int             (*gl_in_hook)() = 0;
  64. int             (*gl_out_hook)() = 0;
  65. int             (*gl_tab_hook)() = gl_tab;
  66.  
  67. /******************** internal interface *********************************/
  68.  
  69. #define BUF_SIZE 1024
  70.  
  71. static int      gl_init_done = -1;      /* terminal mode flag  */
  72. static int      gl_termw = 80;          /* actual terminal width */
  73. static int      gl_scroll = 27;         /* width of EOL scrolling region */
  74. static int      gl_width = 0;           /* net size available for input */
  75. static int      gl_extent = 0;          /* how far to redraw, 0 means all */
  76. static int      gl_overwrite = 0;       /* overwrite mode */
  77. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  78. static char     gl_buf[BUF_SIZE];       /* input buffer */
  79. static char     gl_killbuf[BUF_SIZE]=""; /* killed text */
  80. static char    *gl_prompt;              /* to save the prompt string */
  81. static char     gl_intrc = 0;           /* keyboard SIGINT char */
  82. static char     gl_quitc = 0;           /* keyboard SIGQUIT char */
  83. static char     gl_suspc = 0;           /* keyboard SIGTSTP char */
  84. static char     gl_dsuspc = 0;          /* delayed SIGTSTP char */
  85. static int      gl_search_mode = 0;     /* search mode flag */
  86.  
  87. static void     gl_init();              /* prepare to edit a line */
  88. static void     gl_cleanup();           /* to undo gl_init */
  89. static void     gl_char_init();         /* get ready for no echo input */
  90. static void     gl_char_cleanup();      /* undo gl_char_init */
  91. static size_t   (*gl_strlen)() = (size_t(*)())strlen; 
  92.                                         /* returns printable prompt width */
  93.  
  94. static void     gl_addchar();           /* install specified char */
  95. static void     gl_del();               /* del, either left (-1) or cur (0) */
  96. static void     gl_error();             /* write error msg and die */
  97. static void     gl_fixup();             /* fixup state variables and screen */
  98. static int      gl_getc();              /* read one char from terminal */
  99. static void     gl_kill();              /* delete to EOL */
  100. static void     gl_newline();           /* handle \n or \r */
  101. static void     gl_putc();              /* write one char to terminal */
  102. static void     gl_puts();              /* write a line to terminal */
  103. static void     gl_redraw();            /* issue \n and redraw all */
  104. static void     gl_transpose();         /* transpose two chars */
  105. static void     gl_yank();              /* yank killed text */
  106. static void     gl_word();              /* move a word */
  107.  
  108. static void     hist_init();    /* initializes hist pointers */
  109. static char    *hist_next();    /* return ptr to next item */
  110. static char    *hist_prev();    /* return ptr to prev item */
  111. static char    *hist_save();    /* makes copy of a string, without NL */
  112.  
  113. static void     search_addchar();       /* increment search string */
  114. static void     search_term();          /* reset with current contents */
  115. static void     search_back();          /* look back for current string */
  116. static void     search_forw();          /* look forw for current string */
  117.  
  118. /************************ nonportable part *********************************/
  119.  
  120. extern int      write();
  121. extern void     exit();
  122.  
  123. #ifdef _IBMR2
  124. #define unix
  125. #endif
  126.  
  127. #ifdef MSDOS
  128. #include <bios.h>
  129. #endif
  130.  
  131. #ifdef unix
  132. extern int      read();
  133. extern int      ioctl();
  134.  
  135. #ifdef POSIX            /* use POSIX interface */
  136. #include <termios.h>
  137. struct termios  new_termios, old_termios;
  138. #else /* not POSIX */
  139. #include <sys/ioctl.h>
  140. #ifdef M_XENIX  /* does not really use bsd terminal interface */
  141. #undef TIOCSETN
  142. #endif /* M_XENIX */
  143. #ifdef TIOCSETN         /* use BSD interface */
  144. #include <sgtty.h>
  145. struct sgttyb   new_tty, old_tty;
  146. struct tchars   tch;
  147. struct ltchars  ltch;
  148. #else                   /* use SYSV interface */
  149. #include <termio.h>
  150. struct termio   new_termio, old_termio;
  151. #endif /* TIOCSETN */
  152. #endif /* POSIX */
  153. #endif  /* unix */
  154.  
  155. #ifdef vms
  156. #include <descrip.h>
  157. #include <ttdef.h>
  158. #include <iodef.h>
  159. #include unixio
  160.    
  161. static int   setbuff[2];             /* buffer to set terminal attributes */
  162. static short chan = -1;              /* channel to terminal */
  163. struct dsc$descriptor_s descrip;     /* VMS descriptor */
  164. #endif
  165.  
  166. static void
  167. gl_char_init()                  /* turn off input echo */
  168. {
  169. #ifdef THINK_C
  170.     csetmode(C_RAW,stdin);
  171. #endif
  172. #ifdef unix
  173. #ifdef POSIX
  174.     tcgetattr(0, &old_termios);
  175.     gl_intrc = old_termios.c_cc[VINTR];
  176.     gl_quitc = old_termios.c_cc[VQUIT];
  177. #ifdef VSUSP
  178.     gl_suspc = old_termios.c_cc[VSUSP];
  179. #endif
  180. #ifdef VDSUSP
  181.     gl_dsuspc = old_termios.c_cc[VDSUSP];
  182. #endif
  183.     new_termios = old_termios;
  184.     new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  185.     new_termios.c_iflag |= (IGNBRK|IGNPAR);
  186.     new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  187.     new_termios.c_cc[VMIN] = 1;
  188.     new_termios.c_cc[VTIME] = 0;
  189.     tcsetattr(0, TCSANOW, &new_termios);
  190. #else                           /* not POSIX */
  191. #ifdef TIOCSETN                 /* BSD */
  192.     ioctl(0, TIOCGETC, &tch);
  193.     ioctl(0, TIOCGLTC, <ch);
  194.     gl_intrc = tch.t_intrc;
  195.     gl_quitc = tch.t_quitc;
  196.     gl_suspc = ltch.t_suspc;
  197.     gl_dsuspc = ltch.t_dsuspc;
  198.     ioctl(0, TIOCGETP, &old_tty);
  199.     new_tty = old_tty;
  200.     new_tty.sg_flags |= RAW;
  201.     new_tty.sg_flags &= ~ECHO;
  202.     ioctl(0, TIOCSETN, &new_tty);
  203. #else                           /* SYSV */
  204.     ioctl(0, TCGETA, &old_termio);
  205.     gl_intrc = old_termio.c_cc[VINTR];
  206.     gl_quitc = old_termio.c_cc[VQUIT];
  207.     new_termio = old_termio;
  208.     new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  209.     new_termio.c_iflag |= (IGNBRK|IGNPAR);
  210.     new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
  211.     new_termio.c_cc[VMIN] = 1;
  212.     new_termio.c_cc[VTIME] = 0;
  213.     ioctl(0, TCSETA, &new_termio);
  214. #endif /* TIOCSETN */
  215. #endif /* POSIX */
  216. #endif /* unix */
  217.  
  218. #ifdef vms
  219.     descrip.dsc$w_length  = strlen("tt:");
  220.     descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  221.     descrip.dsc$b_class   = DSC$K_CLASS_S;
  222.     descrip.dsc$a_pointer = "tt:";
  223.     (void)sys$assign(&descrip,&chan,0,0);
  224.     (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  225.     setbuff[1] |= TT$M_NOECHO;
  226.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  227. #endif /* vms */
  228. }
  229.  
  230. static void
  231. gl_char_cleanup()               /* undo effects of gl_char_init */
  232. {
  233. #ifdef THINK_C
  234.     csetmode(C_CBREAK, stdin);
  235. #endif
  236.  
  237. #ifdef unix
  238. #ifdef POSIX 
  239.     tcsetattr(0, TCSANOW, &old_termios);
  240. #else                   /* not POSIX */
  241. #ifdef TIOCSETN         /* BSD */
  242.     ioctl(0, TIOCSETN, &old_tty);
  243. #else                   /* SYSV */
  244.     ioctl(0, TCSETA, &old_termio);
  245. #endif /* TIOCSETN */
  246. #endif /* POSIX */
  247. #endif /* unix */
  248.  
  249. #ifdef vms
  250.     setbuff[1] &= ~TT$M_NOECHO;
  251.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  252.     sys$dassgn(chan);
  253.     chan = -1;
  254. #endif 
  255. }
  256.  
  257. #if MSDOS || __EMX__
  258. int pc_keymap(c)
  259. int c;
  260. {
  261.     switch (c) {
  262.     case 72: c = 16;   /* up -> ^P */
  263.         break;
  264.     case 80: c = 14;   /* down -> ^N */
  265.         break;
  266.     case 75: c = 2;    /* left -> ^B */
  267.         break;
  268.     case 77: c = 6;    /* right -> ^F */
  269.         break;
  270.     default: c = 0;    /* make it garbage */
  271.     }
  272.     return c;
  273. }
  274. #endif /* MSDOS || __EMCX__ */
  275.  
  276. static int
  277. gl_getc()
  278. /* get a character without echoing it to screen */
  279. {
  280.     int             c;
  281.     char            ch;
  282. #ifdef THINK_C
  283.     csetcursor(0); 
  284.     while((c = getchar()) == EOF)
  285.         ;
  286. #endif
  287. #ifdef unix
  288.     c = (read(0, &ch, 1) > 0)? ch : -1;
  289. #endif
  290. #ifdef MSDOS
  291.     c = _bios_keybrd(_NKEYBRD_READ);
  292.     if ((c & 0377) == 224) {
  293.         c = pc_keymap((c >> 8) & 0377);
  294.     } else {
  295.         c &= 0377;
  296.     }
  297. #endif
  298. #ifdef __TURBOC__
  299.     while(!bioskey(1))
  300.         ;
  301.     c = bioskey(0) & 0xff;
  302. #endif
  303. #ifdef __EMX__
  304. #define getch() _read_kbd(0, 1, 0)
  305.     c = getch();
  306.     if (c == 224 || c == 0) {
  307.         c = pc_keymap(getch());
  308.     } else {
  309.         c &= 0377;
  310.     }
  311. #endif
  312. #ifdef vms
  313.     if(chan < 0) {
  314.        c='\0';
  315.     }
  316.     (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  317.     c &= 0177;                        /* get a char */
  318. #endif
  319.     return c;
  320. }
  321.  
  322. static void
  323. gl_putc(c)
  324. int     c;
  325. {
  326. #ifdef THINK_C
  327.     putchar(c);
  328.     csetcursor(0);
  329. #else
  330.     char   ch = c;
  331.     write(1, &ch, 1);
  332.     if (ch == '\n') {
  333.     ch = '\r';
  334.         write(1, &ch, 1);   /* RAW mode needs '\r', does not hurt */
  335.     }
  336. #endif
  337. }
  338.  
  339. /******************** fairly portable part *********************************/
  340.  
  341. static void
  342. gl_puts(buf)
  343. char *buf;
  344. {
  345.     int len; 
  346. #ifdef THINK_C
  347.     if (buf)  {         
  348.        fprintf(stdout,"%s",buf);     
  349.        csetcursor(0);
  350.     }
  351. #else    
  352.     if (buf) {
  353.        len = strlen(buf);
  354.        write(1, buf, len);
  355.     }
  356. #endif
  357. }
  358.  
  359. static void
  360. gl_error(buf)
  361. char *buf;
  362. {
  363. #ifdef THINK_C
  364.     fprintf(stderr,"%s",buf);   
  365. #else  
  366.     int len = strlen(buf);
  367.  
  368.     gl_cleanup();
  369.     write(2, buf, len);
  370.     exit(1);
  371. #endif
  372. }
  373.  
  374. static void
  375. gl_init()
  376. /* set up variables and terminal */
  377. {
  378.     if (gl_init_done < 0) {             /* -1 only on startup */
  379.         hist_init();
  380.     }
  381.     if (isatty(0) == 0 || isatty(1) == 0)
  382.         gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
  383.     gl_char_init();
  384.     gl_init_done = 1;
  385. }
  386.  
  387. static void
  388. gl_cleanup()
  389. /* undo effects of gl_init, as necessary */
  390. {
  391.     if (gl_init_done > 0)
  392.         gl_char_cleanup();
  393.     gl_init_done = 0;
  394. }
  395.  
  396. void
  397. gl_setwidth(w)
  398. int  w;
  399. {
  400.     if (w > 20) {
  401.         gl_termw = w;
  402.         gl_scroll = w / 3;
  403.     } else {
  404.         gl_error("\n*** Error: minimum screen width is 21\n");
  405.     }
  406. }
  407.  
  408. char *
  409. getline(prompt)
  410. char *prompt;
  411. {
  412.     int             c, loc, tmp;
  413.     int             sig;
  414. #ifdef THINK_C  
  415.     char *pause = "pause();\n";
  416. #endif
  417.  
  418.     gl_init();  
  419.     gl_prompt = (prompt)? prompt : "";
  420.     gl_buf[0] = 0;
  421.     if (gl_in_hook)
  422.         gl_in_hook(gl_buf);
  423.     gl_fixup(gl_prompt, -2, BUF_SIZE);
  424.     while ((c = gl_getc()) >= 0) {
  425.         gl_extent = 0;          /* reset to full extent */
  426.         if (isprint(c)) {
  427.             if (gl_search_mode)
  428.                search_addchar(c);
  429.             else
  430.                gl_addchar(c);
  431.         } else {
  432.             if (gl_search_mode) {
  433.                 if (c == '\033' || c == '\016' || c == '\020') {
  434.                     search_term();
  435.                     c = 0;              /* ignore the character */
  436.                 } else if (c == '\010' || c == '\177') {
  437.                     search_addchar(-1); /* unwind search string */
  438.                     c = 0;
  439.                 } else if (c != '\022' && c != '\023') {
  440.                     search_term();      /* terminate and handle char */
  441.                 }
  442.             }
  443.             switch (c) {
  444.               case '\n': case '\r':                     /* newline */
  445.                 gl_newline();
  446.                 gl_cleanup();
  447.                 return gl_buf;
  448.                 /*NOTREACHED*/
  449.                 break; 
  450.               case '\001': gl_fixup(gl_prompt, -1, 0);          /* ^A */
  451.                 break;
  452.               case '\002': gl_fixup(gl_prompt, -1, gl_pos-1);   /* ^B */
  453.                 break;
  454.               case '\004':                                      /* ^D */
  455.                 if (gl_cnt == 0) {
  456.                     gl_buf[0] = 0;
  457.                     gl_cleanup();
  458.                     gl_putc('\n');
  459.                     return gl_buf;
  460.                 } else {
  461.                     gl_del(0);
  462.                 }
  463.                 break;
  464.               case '\005': gl_fixup(gl_prompt, -1, gl_cnt);     /* ^E */
  465.                 break;
  466.               case '\006': gl_fixup(gl_prompt, -1, gl_pos+1);   /* ^F */
  467.                 break;
  468. #ifdef THINK_C
  469.               case '\010': gl_del(-1);                          /* ^H and DEL */
  470.                 break;
  471.               case '\177': gl_del(0);                           /* ->DEL  */
  472.                 break;
  473. #else       
  474.               case '\010': case '\177': gl_del(-1);             /* ^H and DEL */
  475.                 break;
  476. #endif
  477.               case '\t':                                        /* TAB */
  478.                 if (gl_tab_hook) {
  479.                     tmp = gl_pos;
  480.                     loc = gl_tab_hook(gl_buf, gl_strlen(gl_prompt), &tmp);
  481.                     if (loc >= 0 || tmp != gl_pos)
  482.                         gl_fixup(gl_prompt, loc, tmp);
  483.                 }
  484.                 break;
  485.               case '\013': gl_kill(gl_pos);                     /* ^K */
  486.                 break;
  487.               case '\014': gl_redraw();                         /* ^L */
  488.                 break;
  489.               case '\016':                                      /* ^N */
  490.                 strcpy(gl_buf, hist_next());
  491.                 if (gl_in_hook)
  492.                     gl_in_hook(gl_buf);
  493.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  494.                 break;
  495.               case '\017': gl_overwrite = !gl_overwrite;        /* ^O */
  496.                 break;
  497.               case '\020':                                      /* ^P */
  498.                 strcpy(gl_buf, hist_prev());
  499.                 if (gl_in_hook)
  500.                     gl_in_hook(gl_buf);
  501.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  502.                 break;
  503.               case '\022': search_back(1);                      /* ^R */
  504.                 break;
  505.               case '\023': search_forw(1);                      /* ^S */
  506.                 break;
  507.               case '\024': gl_transpose();                      /* ^T */
  508.                 break;
  509.               case '\025': gl_kill(0);                          /* ^U */
  510.                 break;
  511.               case '\031': gl_yank();                           /* ^Y */
  512.                 break;
  513. #ifdef THINK_C
  514.              /* Mac arrow keys */
  515.              case '\x1E':                                      /* up */
  516.                 strcpy(gl_buf, hist_prev());
  517.                 if (gl_in_hook)
  518.                    gl_in_hook(gl_buf);
  519.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  520.                 break;
  521.              case '\x1F':                                      /* down */
  522.                 strcpy(gl_buf, hist_next());
  523.                 if (gl_in_hook)
  524.                    gl_in_hook(gl_buf);
  525.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  526.                 break;
  527.              case '\x1D': gl_fixup(gl_prompt, -1, gl_pos+1);   /* right */
  528.                 break;
  529.              case '\x1C': gl_fixup(gl_prompt, -1, gl_pos-1);   /* left */
  530.                 break;
  531.              /* esecape code */
  532.              case '\033':                           
  533.                 c = gl_getc();
  534.                 if (c == 'f' || c == 'F') {                  /* esc-F */
  535.                   gl_word(1);
  536.                 } else if (c == 'b' || c == 'B') {           /* esc-B */
  537.                   gl_word(-1);
  538.                 } else if (c == 'p' || c == 'P' ||c == '`' || c == '~') { /* esc-P pause */
  539.                   gl_putc('\n');
  540.                   gl_cleanup();
  541.                   return pause;                           
  542.                 } else
  543.                   gl_putc('\007');
  544.                 break;
  545.              /* Mac Menu functions */
  546.              case '\021':                                      /* quit */
  547.                 exit(0);
  548.              case '\032':                                      /* undo */
  549.              case '\030':                                      /* cut  */
  550.              case '\003':                                      /* copy */
  551.              case '\026':                                      /* paste */
  552.                putchar(c);
  553.                break;
  554. #else       
  555.               case '\033':                              /* ansi arrow keys */
  556.                 c = gl_getc();
  557.                 if (c == '[') {
  558.                     switch(c = gl_getc()) {
  559.                       case 'A':                                 /* up */
  560.                         strcpy(gl_buf, hist_prev());
  561.                         if (gl_in_hook)
  562.                             gl_in_hook(gl_buf);
  563.                         gl_fixup(gl_prompt, 0, BUF_SIZE);
  564.                         break;
  565.                       case 'B':                                 /* down */
  566.                         strcpy(gl_buf, hist_next());
  567.                         if (gl_in_hook)
  568.                             gl_in_hook(gl_buf);
  569.                         gl_fixup(gl_prompt, 0, BUF_SIZE);
  570.                         break;
  571.                       case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
  572.                         break;
  573.                       case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
  574.                         break;
  575.                       default: gl_putc('\007');         /* who knows */
  576.                         break;
  577.                     }
  578.                 } else if (c == 'f' || c == 'F') {
  579.                     gl_word(1);
  580.                 } else if (c == 'b' || c == 'B') {
  581.                     gl_word(-1);
  582.                 } else
  583.                     gl_putc('\007');
  584.                 break;
  585. #endif
  586.               default:          /* check for a terminal signal */
  587. #ifdef unix
  588.                 if (c > 0) {    /* ignore 0 (reset above) */
  589.                     sig = 0;
  590. #ifdef SIGINT
  591.                     if (c == gl_intrc)
  592.                         sig = SIGINT;
  593. #endif
  594. #ifdef SIGQUIT
  595.                     if (c == gl_quitc)
  596.                         sig = SIGQUIT;
  597. #endif
  598. #ifdef SIGTSTP
  599.                     if (c == gl_suspc || c == gl_dsuspc)
  600.                         sig = SIGTSTP;
  601. #endif
  602.                     if (sig != 0) {
  603.                         gl_cleanup();
  604.                         kill(0, sig);
  605.                         gl_init();
  606.                         gl_redraw();
  607.                         c = 0;
  608.                     } 
  609.                 }
  610. #endif /* unix */
  611.                 if (c > 0)
  612.                     gl_putc('\007');
  613.                 break;
  614.             }
  615.         }
  616.     }
  617.     gl_cleanup();
  618.     gl_buf[0] = 0;
  619.     return gl_buf;
  620. }
  621.  
  622. static void
  623. gl_addchar(c)
  624. int c;
  625. /* adds the character c to the input buffer at current location */
  626. {
  627.     int  i;
  628.  
  629.     if (gl_cnt >= BUF_SIZE - 1)
  630.         gl_error("\n*** Error: getline(): input buffer overflow\n");
  631.     if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  632.         for (i=gl_cnt; i >= gl_pos; i--)
  633.             gl_buf[i+1] = gl_buf[i];
  634.         gl_buf[gl_pos] = c;
  635.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  636.     } else {
  637.         gl_buf[gl_pos] = c;
  638.         gl_extent = 1;
  639.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  640.     }
  641. }
  642.  
  643. static void
  644. gl_yank()
  645. /* adds the kill buffer to the input buffer at current location */
  646. {
  647.     int  i, len;
  648.  
  649.     len = strlen(gl_killbuf);
  650.     if (len > 0) {
  651.         if (gl_overwrite == 0) {
  652.             if (gl_cnt + len >= BUF_SIZE - 1) 
  653.                 gl_error("\n*** Error: getline(): input buffer overflow\n");
  654.             for (i=gl_cnt; i >= gl_pos; i--)
  655.                 gl_buf[i+len] = gl_buf[i];
  656.             for (i=0; i < len; i++)
  657.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  658.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  659.         } else {
  660.             if (gl_pos + len > gl_cnt) {
  661.                 if (gl_pos + len >= BUF_SIZE - 1) 
  662.                     gl_error("\n*** Error: getline(): input buffer overflow\n");
  663.                 gl_buf[gl_pos + len] = 0;
  664.             }
  665.             for (i=0; i < len; i++)
  666.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  667.             gl_extent = len;
  668.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  669.         }
  670.     } else
  671.         gl_putc('\007');
  672. }
  673.  
  674. static void
  675. gl_transpose()
  676. /* switch character under cursor and to left of cursor */
  677. {
  678.     int    c;
  679.  
  680.     if (gl_pos > 0 && gl_cnt > gl_pos) {
  681.         c = gl_buf[gl_pos-1];
  682.         gl_buf[gl_pos-1] = gl_buf[gl_pos];
  683.         gl_buf[gl_pos] = c;
  684.         gl_extent = 2;
  685.         gl_fixup(gl_prompt, gl_pos-1, gl_pos);
  686.     } else
  687.         gl_putc('\007');
  688. }
  689.  
  690. static void
  691. gl_newline()
  692. /*
  693.  * Cleans up entire line before returning to caller. A \n is appended.
  694.  * If line longer than screen, we redraw starting at beginning
  695.  */
  696. {
  697.     int change = gl_cnt;
  698.     int len = gl_cnt;
  699.     int loc = gl_width - 5;     /* shifts line back to start position */
  700.  
  701.     if (gl_cnt >= BUF_SIZE - 1) 
  702.         gl_error("\n*** Error: getline(): input buffer overflow\n");
  703.     if (gl_out_hook) {
  704.         change = gl_out_hook(gl_buf);
  705.         len = strlen(gl_buf);
  706.     } 
  707.     if (loc > len)
  708.         loc = len;
  709.     gl_fixup(gl_prompt, change, loc);   /* must do this before appending \n */
  710.     gl_buf[len] = '\n';
  711.     gl_buf[len+1] = '\0';
  712.     gl_putc('\n');
  713. }
  714.  
  715. static void
  716. gl_del(loc)
  717. int loc;
  718. /*
  719.  * Delete a character.  The loc variable can be:
  720.  *    -1 : delete character to left of cursor
  721.  *     0 : delete character under cursor
  722.  */
  723. {
  724.     int i;
  725.  
  726.     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  727.         for (i=gl_pos+loc; i < gl_cnt; i++)
  728.             gl_buf[i] = gl_buf[i+1];
  729.         gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
  730.     } else
  731.         gl_putc('\007');
  732. }
  733.  
  734. static void
  735. gl_kill(pos)
  736. int pos;
  737. /* delete from pos to the end of line */
  738. {
  739.     if (pos < gl_cnt) {
  740.         strcpy(gl_killbuf, gl_buf + pos);
  741.         gl_buf[pos] = '\0';
  742.         gl_fixup(gl_prompt, pos, pos);
  743.     } else
  744.         gl_putc('\007');
  745. }
  746.  
  747. static void
  748. gl_word(direction)
  749. int direction;
  750. /* move forward or backword one word */
  751. {
  752.     int pos = gl_pos;
  753.  
  754.     if (direction > 0) {                /* forward */
  755.         while (!isspace(gl_buf[pos]) && pos < gl_cnt) 
  756.             pos++;
  757.         while (isspace(gl_buf[pos]) && pos < gl_cnt)
  758.             pos++;
  759.     } else {                            /* backword */
  760.         if (pos > 0)
  761.             pos--;
  762.         while (isspace(gl_buf[pos]) && pos > 0)
  763.             pos--;
  764.         while (!isspace(gl_buf[pos]) && pos > 0) 
  765.             pos--;
  766.         if (pos < gl_cnt && isspace(gl_buf[pos]))   /* move onto word */
  767.             pos++;
  768.     }
  769.     gl_fixup(gl_prompt, -1, pos);
  770. }
  771.  
  772. static void
  773. gl_redraw()
  774. /* emit a newline, reset and redraw prompt and current input line */
  775. {
  776.     if (gl_init_done > 0) {
  777.         gl_putc('\n');
  778.         gl_fixup(gl_prompt, -2, gl_pos);
  779.     }
  780. }
  781.  
  782. static void
  783. gl_fixup(prompt, change, cursor)
  784. char  *prompt;
  785. int    change, cursor;
  786. /*
  787.  * This function is used both for redrawing when input changes or for
  788.  * moving within the input line.  The parameters are:
  789.  *   prompt:  compared to last_prompt[] for changes;
  790.  *   change : the index of the start of changes in the input buffer,
  791.  *            with -1 indicating no changes, -2 indicating we're on
  792.  *            a new line, redraw everything.
  793.  *   cursor : the desired location of the cursor after the call.
  794.  *            A value of BUF_SIZE can be used  to indicate the cursor should
  795.  *            move just past the end of the input line.
  796.  */
  797. {
  798.     static int   gl_shift;      /* index of first on screen character */
  799.     static int   off_right;     /* true if more text right of screen */
  800.     static int   off_left;      /* true if more text left of screen */
  801.     static char  last_prompt[80] = "";
  802.     int          left = 0, right = -1;          /* bounds for redraw */
  803.     int          pad;           /* how much to erase at end of line */
  804.     int          backup;        /* how far to backup before fixing */
  805.     int          new_shift;     /* value of shift based on cursor */
  806.     int          extra;         /* adjusts when shift (scroll) happens */
  807.     int          i;
  808.     int          new_right = -1; /* alternate right bound, using gl_extent */
  809.     int          l1, l2;
  810.  
  811.     if (change == -2) {   /* reset */
  812.         gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
  813.         gl_putc('\r');
  814.         gl_puts(prompt);
  815.         strcpy(last_prompt, prompt);
  816.         change = 0;
  817.         gl_width = gl_termw - gl_strlen(prompt);
  818.     } else if (strcmp(prompt, last_prompt) != 0) {
  819.         l1 = gl_strlen(last_prompt);
  820.         l2 = gl_strlen(prompt);
  821.         gl_cnt = gl_cnt + l1 - l2;
  822.         strcpy(last_prompt, prompt);
  823.         gl_putc('\r');
  824.         gl_puts(prompt);
  825.         gl_pos = gl_shift;
  826.         gl_width = gl_termw - l2;
  827.         change = 0;
  828.     }
  829.     pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  830.     backup = gl_pos - gl_shift;
  831.     if (change >= 0) {
  832.         gl_cnt = strlen(gl_buf);
  833.         if (change > gl_cnt)
  834.             change = gl_cnt;
  835.     }
  836.     if (cursor > gl_cnt) {
  837.         if (cursor != BUF_SIZE)         /* BUF_SIZE means end of line */
  838.             gl_putc('\007');
  839.         cursor = gl_cnt;
  840.     }
  841.     if (cursor < 0) {
  842.         gl_putc('\007');
  843.         cursor = 0;
  844.     }
  845.     if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
  846.         extra = 2;                      /* shift the scrolling boundary */
  847.     else 
  848.         extra = 0;
  849.     new_shift = cursor + extra + gl_scroll - gl_width;
  850.     if (new_shift > 0) {
  851.         new_shift /= gl_scroll;
  852.         new_shift *= gl_scroll;
  853.     } else
  854.         new_shift = 0;
  855.     if (new_shift != gl_shift) {        /* scroll occurs */
  856.         gl_shift = new_shift;
  857.         off_left = (gl_shift)? 1 : 0;
  858.         off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  859.         left = gl_shift;
  860.         new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  861.     } else if (change >= 0) {           /* no scroll, but text changed */
  862.         if (change < gl_shift + off_left) {
  863.             left = gl_shift;
  864.         } else {
  865.             left = change;
  866.             backup = gl_pos - change;
  867.         }
  868.         off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  869.         right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  870.         new_right = (gl_extent && (right > left + gl_extent))? 
  871.                      left + gl_extent : right;
  872.     }
  873.     pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  874.     pad = (pad < 0)? 0 : pad;
  875.     if (left <= right) {                /* clean up screen */
  876.         for (i=0; i < backup; i++)
  877.             gl_putc('\b');
  878.         if (left == gl_shift && off_left) {
  879.             gl_putc('$');
  880.             left++;
  881.         }
  882.         for (i=left; i < new_right; i++)
  883.             gl_putc(gl_buf[i]);
  884.         gl_pos = new_right;
  885.         if (off_right && new_right == right) {
  886.             gl_putc('$');
  887.             gl_pos++;
  888.         } else { 
  889.             for (i=0; i < pad; i++)     /* erase remains of prev line */
  890.                 gl_putc(' ');
  891.             gl_pos += pad;
  892.         }
  893.     }
  894.     i = gl_pos - cursor;                /* move to final cursor location */
  895.     if (i > 0) {
  896.         while (i--)
  897.            gl_putc('\b');
  898.     } else {
  899.         for (i=gl_pos; i < cursor; i++)
  900.             gl_putc(gl_buf[i]);
  901. #ifdef THINK_C
  902.        /* just to show cursor */
  903.        csetmode(C_CBREAK,stdin);
  904.        csetmode(C_RAW,stdin);
  905. #endif
  906.     }
  907.     gl_pos = cursor;
  908. }
  909.  
  910. static int
  911. gl_tab(buf, offset, loc)
  912. char  *buf;
  913. int    offset;
  914. int   *loc;
  915. /* default tab handler, acts like tabstops every 8 cols */
  916. {
  917.     int i, count, len;
  918.  
  919.     len = strlen(buf);
  920.     count = 8 - (offset + *loc) % 8;
  921.     for (i=len; i >= *loc; i--)
  922.         buf[i+count] = buf[i];
  923.     for (i=0; i < count; i++)
  924.         buf[*loc+i] = ' ';
  925.     i = *loc;
  926.     *loc = i + count;
  927.     return i;
  928. }
  929.  
  930. /******************* strlen stuff **************************************/
  931.  
  932. void gl_strwidth(func)
  933. size_t (*func)();
  934. {
  935.     if (func != 0) {
  936.         gl_strlen = func;
  937.     }
  938. }
  939.  
  940. /******************* History stuff **************************************/
  941.  
  942. #ifndef HIST_SIZE
  943. #define HIST_SIZE 100
  944. #endif
  945.  
  946. static int      hist_pos = 0, hist_last = 0;
  947. static char    *hist_buf[HIST_SIZE];
  948.  
  949. static void
  950. hist_init()
  951. {
  952.     int i;
  953.  
  954.     hist_buf[0] = "";
  955.     for (i=1; i < HIST_SIZE; i++)
  956.         hist_buf[i] = (char *)0;
  957. }
  958.  
  959. void
  960. gl_histadd(buf)
  961. char *buf;
  962. {
  963.     static char *prev = 0;
  964.     char *p = buf;
  965.     int len;
  966.  
  967.     /* in case we call gl_histadd() before we call getline() */
  968.     if (gl_init_done < 0) {             /* -1 only on startup */
  969.         hist_init();
  970.         gl_init_done = 0;
  971.     }
  972.     while (*p == ' ' || *p == '\t' || *p == '\n') 
  973.         p++;
  974.     if (*p) {
  975.         len = strlen(buf);
  976.         if (strchr(p, '\n'))    /* previously line already has NL stripped */
  977.             len--;
  978.         if (prev == 0 || strlen(prev) != len || 
  979.                             strncmp(prev, buf, len) != 0) {
  980.             hist_buf[hist_last] = hist_save(buf);
  981.             prev = hist_buf[hist_last];
  982.             hist_last = (hist_last + 1) % HIST_SIZE;
  983.             if (hist_buf[hist_last] && *hist_buf[hist_last]) {
  984.                 free(hist_buf[hist_last]);
  985.             }
  986.             hist_buf[hist_last] = "";
  987.         }
  988.     }
  989.     hist_pos = hist_last;
  990. }
  991.  
  992. static char *
  993. hist_prev()
  994. /* loads previous hist entry into input buffer, sticks on first */
  995. {
  996.     char *p = 0;
  997.     int   next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  998.  
  999.     if (hist_buf[hist_pos] != 0 && next != hist_last) {
  1000.         hist_pos = next;
  1001.         p = hist_buf[hist_pos];
  1002.     } 
  1003.     if (p == 0) {
  1004.         p = "";
  1005.         gl_putc('\007');
  1006.     }
  1007.     return p;
  1008. }
  1009.  
  1010. static char *
  1011. hist_next()
  1012. /* loads next hist entry into input buffer, clears on last */
  1013. {
  1014.     char *p = 0;
  1015.  
  1016.     if (hist_pos != hist_last) {
  1017.         hist_pos = (hist_pos+1) % HIST_SIZE;
  1018.         p = hist_buf[hist_pos];
  1019.     } 
  1020.     if (p == 0) {
  1021.         p = "";
  1022.         gl_putc('\007');
  1023.     }
  1024.     return p;
  1025. }
  1026.  
  1027. static char *
  1028. hist_save(p)
  1029. char *p;
  1030. /* makes a copy of the string */
  1031. {
  1032.     char *s = 0;
  1033.     int   len = strlen(p);
  1034.     char *nl = strchr(p, '\n');
  1035.  
  1036.     if (nl) {
  1037.         if ((s = malloc(len)) != 0) {
  1038.             strncpy(s, p, len-1);
  1039.             s[len-1] = 0;
  1040.         }
  1041.     } else {
  1042.         if ((s = malloc(len+1)) != 0) {
  1043.             strcpy(s, p);
  1044.         }
  1045.     }
  1046.     if (s == 0) 
  1047.         gl_error("\n*** Error: hist_save() failed on malloc\n");
  1048.     return s;
  1049. }
  1050.  
  1051. /******************* Search stuff **************************************/
  1052.  
  1053. static char  search_prompt[101];  /* prompt includes search string */
  1054. static char  search_string[100];
  1055. static int   search_pos = 0;      /* current location in search_string */
  1056. static int   search_forw_flg = 0; /* search direction flag */
  1057. static int   search_last = 0;     /* last match found */
  1058.  
  1059. static void  
  1060. search_update(c)
  1061. int c;
  1062. {
  1063.     if (c == 0) {
  1064.         search_pos = 0;
  1065.         search_string[0] = 0;
  1066.         search_prompt[0] = '?';
  1067.         search_prompt[1] = ' ';
  1068.         search_prompt[2] = 0;
  1069.     } else if (c > 0) {
  1070.         search_string[search_pos] = c;
  1071.         search_string[search_pos+1] = 0;
  1072.         search_prompt[search_pos] = c;
  1073.         search_prompt[search_pos+1] = '?';
  1074.         search_prompt[search_pos+2] = ' ';
  1075.         search_prompt[search_pos+3] = 0;
  1076.         search_pos++;
  1077.     } else {
  1078.         if (search_pos > 0) {
  1079.             search_pos--;
  1080.             search_string[search_pos] = 0;
  1081.             search_prompt[search_pos] = '?';
  1082.             search_prompt[search_pos+1] = ' ';
  1083.             search_prompt[search_pos+2] = 0;
  1084.         } else {
  1085.             gl_putc('\007');
  1086.             hist_pos = hist_last;
  1087.         }
  1088.     }
  1089. }
  1090.  
  1091. static void 
  1092. search_addchar(c)
  1093. int  c;
  1094. {
  1095.     char *loc;
  1096.  
  1097.     search_update(c);
  1098.     if (c < 0) {
  1099.         if (search_pos > 0) {
  1100.             hist_pos = search_last;
  1101.         } else {
  1102.             gl_buf[0] = 0;
  1103.             hist_pos = hist_last;
  1104.         }
  1105.         strcpy(gl_buf, hist_buf[hist_pos]);
  1106.     }
  1107.     if ((loc = rstrstr(gl_buf, search_string)) != 0) {
  1108.         gl_fixup(search_prompt, 0, loc - gl_buf);
  1109.     } else if (search_pos > 0) {
  1110.         if (search_forw_flg) {
  1111.             search_forw(0);
  1112.         } else {
  1113.             search_back(0);
  1114.         }
  1115.     } else {
  1116.         gl_fixup(search_prompt, 0, 0);
  1117.     }
  1118. }
  1119.  
  1120. static void     
  1121. search_term()
  1122. {
  1123.     gl_search_mode = 0;
  1124.     if (gl_buf[0] == 0)         /* not found, reset hist list */
  1125.         hist_pos = hist_last;
  1126.     if (gl_in_hook)
  1127.         gl_in_hook(gl_buf);
  1128.     gl_fixup(gl_prompt, 0, gl_pos);
  1129. }
  1130.  
  1131. static void     
  1132. search_back(new_search)
  1133. int new_search;
  1134. {
  1135.     int    found = 0;
  1136.     char  *p, *loc;
  1137.  
  1138.     search_forw_flg = 0;
  1139.     if (gl_search_mode == 0) {
  1140.         search_last = hist_pos = hist_last;     
  1141.         search_update(0);       
  1142.         gl_search_mode = 1;
  1143.         gl_buf[0] = 0;
  1144.         gl_fixup(search_prompt, 0, 0);
  1145.     } else if (search_pos > 0) {
  1146.         while (!found) {
  1147.             p = hist_prev();
  1148.             if (*p == 0) {              /* not found, done looking */
  1149.                gl_buf[0] = 0;
  1150.                gl_fixup(search_prompt, 0, 0);
  1151.                found = 1;
  1152.             } else if ((loc = rstrstr(p, search_string)) != 0) {
  1153.                strcpy(gl_buf, p);
  1154.                gl_fixup(search_prompt, 0, loc - p);
  1155.                if (new_search)
  1156.                    search_last = hist_pos;
  1157.                found = 1;
  1158.             } 
  1159.         }
  1160.     } else {
  1161.         gl_putc('\007');
  1162.     }
  1163. }
  1164.  
  1165. static void     
  1166. search_forw(new_search)
  1167. int new_search;
  1168. {
  1169.     int    found = 0;
  1170.     char  *p, *loc;
  1171.  
  1172.     search_forw_flg = 1;
  1173.     if (gl_search_mode == 0) {
  1174.         search_last = hist_pos = hist_last;     
  1175.         search_update(0);       
  1176.         gl_search_mode = 1;
  1177.         gl_buf[0] = 0;
  1178.         gl_fixup(search_prompt, 0, 0);
  1179.     } else if (search_pos > 0) {
  1180.         while (!found) {
  1181.             p = hist_next();
  1182.             if (*p == 0) {              /* not found, done looking */
  1183.                gl_buf[0] = 0;
  1184.                gl_fixup(search_prompt, 0, 0);
  1185.                found = 1;
  1186.             } else if ((loc = rstrstr(p, search_string)) != 0) {
  1187.                strcpy(gl_buf, p);
  1188.                gl_fixup(search_prompt, 0, loc - p);
  1189.                if (new_search)
  1190.                    search_last = hist_pos;
  1191.                found = 1;
  1192.             } 
  1193.         }
  1194.     } else {
  1195.         gl_putc('\007');
  1196.     }
  1197. }
  1198.  
  1199. #ifndef __STDC__
  1200. /*
  1201.  * The following strstr() function has been added to enable the getline
  1202.  * command line interface to function on older Unix platforms that do
  1203.  * not have this function. The source for strstr() was "borrowed" from
  1204.  * the GNU C-library sources.
  1205.  * Ian Searle, 5/17/93
  1206.  */
  1207.  
  1208. /* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
  1209. This file is part of the GNU C Library.
  1210.  
  1211. The GNU C Library is free software; you can redistribute it and/or
  1212. modify it under the terms of the GNU Library General Public License as
  1213. published by the Free Software Foundation; either version 2 of the
  1214. License, or (at your option) any later version.
  1215.  
  1216. The GNU C Library is distributed in the hope that it will be useful,
  1217. but WITHOUT ANY WARRANTY; without even the implied warranty of
  1218. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  1219. Library General Public License for more details.
  1220.  
  1221. You should have received a copy of the GNU Library General Public
  1222. License along with the GNU C Library; see the file COPYING.LIB.  If
  1223. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  1224. Cambridge, MA 02139, USA.  */
  1225.  
  1226. #include <string.h>
  1227.  
  1228. /* Return the first ocurrence of NEEDLE in HAYSTACK.  */
  1229. static char *
  1230. rstrstr (haystack, needle)
  1231.      CONST char *CONST haystack;
  1232.      CONST char *CONST needle;
  1233. {
  1234.   register CONST char *CONST needle_end = strchr(needle, '\0');
  1235.   register CONST char *CONST haystack_end = strchr(haystack, '\0');
  1236.   register CONST size_t needle_len = needle_end - needle;
  1237.   register CONST size_t needle_last = needle_len - 1;
  1238.   register CONST char *begin;
  1239.  
  1240.   if (needle_len == 0)
  1241.     return (char *) haystack_end;
  1242.   if ((size_t) (haystack_end - haystack) < needle_len)
  1243.     return NULL;
  1244.   
  1245.   for (begin = &haystack[needle_last]; begin < haystack_end; ++begin)
  1246.   {
  1247.     register CONST char *n = &needle[needle_last];
  1248.     register CONST char *h = begin;
  1249.     
  1250.     do
  1251.       if (*h != *n)
  1252.         goto loop;              /* continue for loop */
  1253.     while (--n >= needle && --h >= haystack);
  1254.     
  1255.     return (char *) h;
  1256.     
  1257.   loop:;
  1258.   }
  1259.   
  1260.   return NULL;
  1261. }
  1262. #endif
  1263.